package core;

import com.mysql.jdbc.ResultSet;
import dbManagement.*;
import hirondelle.date4j.DateTime;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.sql.SQLException;
import java.util.Date;
import java.util.LinkedList;
import java.util.TimeZone;
import javax.xml.parsers.ParserConfigurationException;
import logManagement.Log4k;
import objects.*;
import mailManagement.Mailer;
import org.richfaces.event.UploadEvent;
import org.richfaces.model.UploadItem;
import org.xml.sax.SAXException;
import ws.Geonames;

/**
 *
 * @author administrator
 */
public class PatientBean implements Serializable{
    private int id; /* the patient ID */
    private String username;
    private String password;
    private String name;
    private String surname;
    private String email;
    private String phone;    
    private EGender gender;
    private DateTime birthdate;
    private String birthplace;
    private String address;
    private String cap;
    private String coords;
    private int parentId;    
    private int chiefTainId;
    private int refDoctorId;
    private String picture;
    private String latitude;
    private String longitude;
    
    private Date calendarBirthdate = new Date();
    
    /*Variables to show message*/
    private boolean registrationCompleted = false;
    private boolean registrationFailed = false;
    
    /* ID del chiamante delle funzioni in cui questo parametro serve. */
    private int callerId;
    
    private LinkedList<VisitBean> myVisits;    
    
    private boolean editable = true;
    
    private String error; /* for Log4k.debug function */
    
    private void setPatientBean (ResultSet r){
        error="Inizio il parsing della query per il singolo paziente";
        Log4k.debug(this.getClass().getName(), error);
        
        try {
            if (r.first()){
                this.id = r.getInt("patient_id");
                this.username = r.getString("username");
                this.password = r.getString("password");
                this.name = r.getString("name");
                this.surname = (r.getString("surname"));
                this.email = r.getString("email_addr");
                this.phone = r.getString("cell_number");
                this.gender = EGender.valueOf(r.getString("gender"));
                this.birthdate = new DateTime (r.getString("birthDate"));
                this.birthplace = r.getString("birthPlace");
                this.address = r.getString("address");
                this.cap = r.getString("cap");
                this.coords = r.getString("coords");
                this.parentId = r.getInt("parent_id");
                this.chiefTainId = r.getInt("chieftain_id");
                this.refDoctorId = r.getInt("refDoctor_id");
                this.picture = r.getString("picture");
                //Recupero coordinate
                
                if ((this.coords != null) && (!this.coords.equals(""))){
                    String splittedcoords[] = coords.split(", ");
                    if(splittedcoords.length == 2){
                        this.latitude = splittedcoords[0];
                        this.longitude = splittedcoords[1];
                    }
                }
            }
            else{
                error="La lista è vuota! Potrebbe essere stato settato l'id errato o essersi verificato un problema nella query.";
                Log4k.warn(this.getClass().getName(), error);
            }
        } catch (SQLException ex) {
            error = "Lanciata SQLException in setPatientBean";
            Log4k.debug(this.getClass().getName(), error);
            Log4k.error(this.getClass().getName(), ex.getMessage());
        }
        error="Fine del parsing.";
        Log4k.debug(this.getClass().getName(), error);
    }
    
    /*
     * Da chiamare dopo aver settato id, refDoctorId e callerId.
     * Recupera i dati relativi all'ID dal database e setta tutti i campi
     *
     * callerId l'id del medico che sta avanzando la richiesta
     */
    public String retrievePatient(){
        error="Chiamata retrivePatient";
        error+="\n callerID: "+this.callerId;
        error+="\n patientID: "+this.id;
        error+="\n doctorID: "+this.refDoctorId;
        Log4k.debug(this.getClass().getName(), error);
        
        if ((this.id != 0) && (this.refDoctorId != 0)){
            dbManager db = new dbManager();
            try {
                /* Qui come userkind passiamo sempre DOCTOR perche' distinguere
                 * tra quello e USL da' più problemi implementativi di quelli
                 * cui e' posto a risolvere */
        
                ResultSet r = db.getPatientByID(this.callerId, this.id, userKind.DOCTOR);
                error="Risiltato di getPatientByID = "+r;
                Log4k.debug(this.getClass().getName(), error);
                this.setPatientBean(r);
                
                this.editable=true;//resetta a non editabile il bean
            } catch (notMyPatientException ex) {
                error="retrievePatient: ";
                Log4k.error(this.getClass().getName(), error+ex.getMessage());
            }
            db.releaseConnection();
            return "profile";
        }
        else{
            String msg = "either doctorID or userID has negative value";
            Log4k.error(this.getClass().getName(), msg);
            return null;
        }
    }
    
    /* Da chiamare dopo aver settato id */
    public void loadMyProfile(){
        Log4k.debug(this.getClass().getName(), "Chiamata loadMyProfile");
        dbManager db = new dbManager();
        this.setPatientBean(db.getPatientByID(this.id, userKind.PATIENT));
        db.releaseConnection();
    }
        
    public void registerPatient(){
        error = "Chiamata registerPatient";
        Log4k.debug(this.getClass().getName(), error);
        
        this.username = null;
        this.password = Macro.generatePassword();
        error = "Password Generata: "+this.password;
        Log4k.debug(this.getClass().getName(), error);
        
        this.flushMessages();
        
        try {
            dbManager db = new dbManager();
            db.registerPatient(this);
            db.releaseConnection();
            error = "Paziente registrato con successo";
            Log4k.debug(this.getClass().getName(), error);
            
            /* Informa il paziente tramite mail che e' appena stato registrato */
            String sender = Macro.SYSTEM_ADDRESS;
            String receivers[] = {this.email};
            String subject = "Sei registrato / You are registered!";      
            String text= "Salve " + this.name + ",\n"
                    + "Le comunichiamo che Lei e' appena stato registrato al"
                    + "nostro sistema!" + "\n"
                    + "----------------------------------------"
                    + "----------------------------------------" + "\n\n"
                    + "Dear " + this.name + ",\n"
                    + "you just got a registration to our system!" + "\n\n"
                    + "username = " + this.username
                    + "password = " + this.password + "\n";
            Mailer.mail(sender, receivers, subject, text);
            error = "Paziente informato della registrazione tramite mail";
            Log4k.debug(this.getClass().getName(), error);
            
            this.setRegistrationCompleted(true);
            
        } catch (RegisterPatientException ex) {
            error = "Error while registering patient: ";
            Log4k.warn(this.getClass().getName(), error+ex.getMessage());
            this.setRegistrationFailed(true);
        }
    }
    
    public void editPatient(){
        dbManager db = new dbManager();
        db.resetPatient(this);
        db.releaseConnection();
    }
    
    public void flushMessages(){
        this.setRegistrationCompleted(false);
        this.setRegistrationFailed(false);
    }
    
    public void blankPatient(){
        this.flushMessages();
        
        this.address = null;
        this.birthdate = null;
        this.birthplace = null;
        this.calendarBirthdate = null;
        this.callerId = 0;
        this.cap = null;
        this.chiefTainId = 0;
        this.coords = null;
        this.email = null;
        this.gender = null;
        this.id = 0;
        this.latitude = null;
        this.longitude = null;
        this.myVisits = null;
        this.name = null;
        this.parentId = 0;
        this.password = null;
        this.phone = null;
        this.picture = null;
        this.refDoctorId = 0;
        this.surname = null;
        this.username = null;
    }
    
    public void retrieveCoords(){
        if(this.latitude == null || this.longitude == null){
            try {
                this.coords = Geonames.getCoordinates(this.birthplace);
                if ((this.coords != null) && (!this.coords.equals(""))){
                    String splittedcoords[] = coords.split(", ");
                    if(splittedcoords.length == 2){
                        this.latitude = splittedcoords[0];
                        this.longitude = splittedcoords[1];
                    }
                }
            } catch (MalformedURLException ex) {
                error = "Malformed exception in retrieveCoords";
                Log4k.warn(this.getClass().getName(), error);
            } catch (IOException ex) {
                error = "in retrieveCoords: "+ex.getMessage();
                Log4k.error(this.getClass().getName(), error);
            } catch (ParserConfigurationException ex) {
                error = "in retrieveCoords: "+ex.getMessage();
                Log4k.warn(this.getClass().getName(), error);
            } catch (SAXException ex) {
                error = "in retrieveCoords: "+ex.getMessage();
                Log4k.error(this.getClass().getName(), error);
            }
        }
    }
    
    /* Chiamata dal paziente stesso, da chiamare dopo aver settato id */
    public void loadMyFutureVisits(){
        error="Chiamata loadMyFutureVisits()";
        Log4k.debug(this.getClass().getName(), error);
        VisitListBean v = new VisitListBean();
        myVisits = v.futureVisits(this.id, userKind.PATIENT);
    }    
    
    /* Chiamata dal medico, da chiamare dopo aver settato id e callerId */
    
    public void loadFutureVisits(){
        error="Chiamata loadFutureVisits()";
        Log4k.debug(this.getClass().getName(), error);
        VisitListBean v = new VisitListBean();
        myVisits = v.futureVisits(this.callerId, this.id, userKind.DOCTOR);
    }
    
    public void loadFirstVisits(){
        error="Chiamata loadFirstVisits()";
        Log4k.debug(this.getClass().getName(), error);
        VisitListBean v = new VisitListBean();
        myVisits = v.firstVisits(this.id, userKind.PATIENT, Macro.MAX_VISITS_NUMBER);
    }
    
    /* Chiamata dal paziente stesso, da chiamare dopo aver settato id */
    public void loadMyPastVisits(){
        VisitListBean v = new VisitListBean();
        myVisits = v.pastVisits(this.id, userKind.PATIENT);
    }    
    
    /* Chiamata dal medico, da chiamare dopo aver settato id e callerId */
    public void loadPastVisits(){
        VisitListBean v = new VisitListBean();
        myVisits = v.pastVisits(this.callerId, this.id, userKind.DOCTOR);
    }
    
    public void toggleEditable(){
        this.editable = ! this.editable;
    }
    
    public void uploadListener(UploadEvent event){
        File uploadedPicture; 
        
        UploadItem item = event.getUploadItem();
        File file = item.getFile();
        uploadedPicture = new File(Macro.localPath(Macro.LOCAL_IMGPATH+item.getFileName()));
        file.renameTo(uploadedPicture);
        
        this.picture = uploadedPicture.getName();
        error = "File Correctly uploader with name: "+this.picture;
        Log4k.debug(this.getClass().getName(), error);
    }
    
    public String wikiLink(){
        String link = "";
        
        if (this.birthplace != null){
            try {
                link = Geonames.getWikiPage(this.birthplace);
            } catch (MalformedURLException ex) {
                error = "Malformed exception in retrieveCoords";
                Log4k.warn(this.getClass().getName(), error);
            } catch (IOException ex) {
                error = "in retrieveCoords: "+ex.getMessage();
                Log4k.error(this.getClass().getName(), error);
            } catch (ParserConfigurationException ex) {
                error = "in retrieveCoords: "+ex.getMessage();
                Log4k.warn(this.getClass().getName(), error);
            } catch (SAXException ex) {
                error = "in retrieveCoords: "+ex.getMessage();
                Log4k.error(this.getClass().getName(), error);
            }
        }
        return link;
    }
    
    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the username
     */
    public String getUsername() {
        return username;
    }

    /**
     * @param username the username to set
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * Once got, the password is set to null
     * @return the password
     */
    public String getPassword() {
        String res = password;
        //password = null;//per qualche ragione non passa la password al DB
        return res;
    }

    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }    
    
    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the surname
     */
    public String getSurname() {
        return surname;
    }

    /**
     * @param surname the surname to set
     */
    public void setSurname(String surname) {
        this.surname = surname;
    }

    /**
     * @return the birthdate
     * Restituisce solo la data come anno-mese-giorno evitando di mostrare
     * l'ora di nascita (che è sempre 00.00.00)
     */
    public DateTime getBirthdate() {
        /*
        DateTime ret = new DateTime(this.birthdate.format("YYYY-MM-DD"));
        return ret;*/
        return this.birthdate;
    }
    /**
     * @param birthdate the birthdate to set
     */    
    public void setBirthdate(DateTime date){
        this.birthdate = date;
    }

    /**
     * @return the address
     */
    public String getAddress() {
        return address;
    }

    /**
     * @param address the address to set
     */
    public void setAddress(String address) {
        this.address = address;
    }

    /**
     * @return the cap
     */
    public String getCap() {
        return cap;
    }

    /**
     * @param cap the cap to set
     */
    public void setCap(String cap) {
        this.cap = cap;
    }

    /**
     * @return the chiefTainD
     */
    public int getChiefTainId() {
        return chiefTainId;
    }

    /**
     * @param chiefTainId the chiefTainId to set
     */
    public void setChiefTainId(int chiefTainId) {
        this.chiefTainId = chiefTainId;
    }

    /**
     * @return the coords
     */    
    public String getCoords() {
        return coords;
    }

    /**
     * @param coords the coords to set
     */    
    public void setCoords(String coords) {
        this.coords = coords;
    }

    /**
     * @return the parentId
     */
    public int getParentId() {
        return parentId;
    }

    /**
     * @param parentId the parentId to set
     */    
    public void setParentId(int parentId) {
        this.parentId = parentId;
    }    
    
    /**
     * @return the phone
     */
    public String getPhone() {
        return phone;
    }

    /**
     * @param phone the phone to set
     */
    public void setPhone(String phone) {
        this.phone = phone;
    }

    /**
     * @return the refDoctor
     */
    public int getRefDoctorId() {
        return refDoctorId;
    }

    /**
     * @param refDoctor the refDoctor to set
     */
    public void setRefDoctorId(int refDoctorId) {
        this.refDoctorId = refDoctorId;
    }

    /**
     * @return the picture
     */
    public String getPicture() {
        return Macro.IMGPATH+picture;
    }
    
    public String getPictureName() {
        return picture;
    }

    /**
     * @param picture the picture to set
     */
    public void setPicture(String picture) {
        this.picture = picture;
    }

    /**
     * @return the latitude
     */
    public String getLatitude() {
        return latitude;
    }

    /**
     * @param latitude the latitude to set
     */
    public void setLatitude(String latitude) {
        this.latitude = latitude;
    }

    /**
     * @return the longitude
     */
    public String getLongitude() {
        return longitude;
    }

    /**
     * @param longitude the longitude to set
     */
    public void setLongitude(String longitude) {
        this.longitude = longitude;
    }

    /**
     * @return the gender
     */
    public char getGender() {
        if (this.gender == EGender.F){
            return 'F';
        }
        else return 'M';
    }
    
    public EGender getGenderEnum() { 
        return this.gender;
    }

    /**
     * @param gender the gender to set
     */
    public void setGender(char gender) {
        switch (gender){
            case 'M': 
                this.gender = EGender.M;
                break;
            case 'm': 
                this.gender = EGender.M;
                break;
            case 'F': 
                this.gender = EGender.F;
                break;
            case 'f': 
                this.gender = EGender.F;
                break;
            default: 
                String error = "Bad char for variable gender! Accepted only M/m or F/f";
                Log4k.warn(this.getClass().getName(), error);                   
        }
    }
    
    public void setGenderEnum(EGender gender) { 
        this.gender=gender;
    }

    /**
     * @return the birthplace
     */
    public String getBirthplace() {
        return birthplace;
    }

    /**
     * @param birthplace the birthplace to set
     */
    public void setBirthplace(String birthplace) {
        this.birthplace = birthplace;
    }

    /**
     * @return the email
     */
    public String getEmail() {
        return email;
    }

    /**
     * @param email the email to set
     */
    public void setEmail(String email) {
        this.email = email;
    }

    /**
     * @return the editable
     */
    public boolean isEditable() {
        return editable;
    }

    /**
     * @param editable the editable to set
     */
    public void setEditable(boolean editable) {
        this.editable = editable;
    }

    /**
     * @return the myVisits
     */
    public LinkedList getMyVisits() {
        return myVisits;
    }

    /**
     * @param myVisits the myVisits to set
     */
    public void setMyVisits(LinkedList myVisits) {
        this.myVisits = myVisits;
    }
    
    /**
     * @return the caller id
     */
    public int getCallerId() {
        return id;
    }

    /**
     * @param id the caller id to set
     */
    public void setCallerId(int callerId) {
        this.callerId = callerId;
    }

    /**
     * @return the calendarBirthdate
     * Utilizzzata per poter leggere le date con rich:calendar.
     * Se birthdate è settato legge il valore in millisecondi e istanzia un 
     * oggetto di tipo Date(millisecondi) che ha lo stesso valore del DateTime
     * altrimenti crea un nuovo oggetto Date che di default ha la data attuale
     */
    public Date getCalendarBirthdate() {
        long milliseconds;
        if (this.birthdate != null){
            milliseconds = this.birthdate.getMilliseconds(TimeZone.getDefault());
            this.calendarBirthdate.setTime(milliseconds);
        }
        else {
            this.calendarBirthdate = new Date();
        }
        
        return this.calendarBirthdate;
    }

    /**
     * @param calendarBirthdate the calendarBirthdate to set
     * Utilizzata per poter settare le date con rich:calendar.
     * legge la data in millisecondi dal tipo Date() e crea un nuovo oggetto
     * DateTime(millisecondi, timezone).
     */
    public void setCalendarBirthdate(Date calendarBirthdate) {
        error = "ricevuta calendarBirthdare="+calendarBirthdate;
        Log4k.debug(this.getClass().getName(), error);
        this.birthdate=DateTime.forInstant(calendarBirthdate.getTime(), TimeZone.getDefault());
        error="birthdate="+this.birthdate;
        Log4k.debug(this.getClass().getName(), error);
    }

    /**
     * @return the registrationCompleted
     */
    public boolean isRegistrationCompleted() {
        return registrationCompleted;
    }

    /**
     * @param registrationCompleted the registrationCompleted to set
     */
    public void setRegistrationCompleted(boolean registrationCompleted) {
        this.registrationCompleted = registrationCompleted;
    }

    /**
     * @return the registrationFailed
     */
    public boolean isRegistrationFailed() {
        return registrationFailed;
    }

    /**
     * @param registrationFailed the registrationFailed to set
     */
    public void setRegistrationFailed(boolean registrationFailed) {
        this.registrationFailed = registrationFailed;
    }
}
